#include "netComponents.h"
#include "tools/cJSON.h"
#include <QtDebug>

#define NODEPROC_RECVSPACE              (2048)    // 接收空间缓冲区大小

nodeClient::nodeClient()
        : m_nServerPort(0)
        , m_pRecvData(nullptr)
        , m_nProc(INVALID_SOCKET)
        , m_nHeart(INVALID_SOCKET)
        , m_bExit(false)
        , m_bInitStatus(false)
{
    m_strNodeID.clear();
    m_strNodeKey.clear();
    m_strServerIP.clear();
    m_strCheckKey.clear();
    m_listSendMsg.clear();
    m_listMsgInfos.clear();
}

nodeClient::~nodeClient()
{
    // 释放资源
	release();
    // 关闭套接字
    closeSocket();
}

// 设置本节点ID和口令
void nodeClient::setParams(std::string strNodeId, std::string strNodeKey)
{
    m_strNodeKey.assign(strNodeKey);
    m_strNodeID.assign(strNodeId);
}

// 初始化客户端，指定服务端IP、端口和口令，连接到服务端
bool nodeClient::initAndConnect(std::string strServerIP, unsigned int uiPort, std::string strCheckKey)
{
	m_bInitStatus = false;

	do
	{
        if(strServerIP.empty() || 0 == uiPort || strCheckKey.empty())
        {
            qDebug() << "[nodeClient::initAndConnect] empty, break";
            break;
        }

        m_strCheckKey.assign(strCheckKey);
        m_strServerIP.assign(strServerIP);
        m_nServerPort = uiPort;

        m_pRecvData = (char *)malloc(NODEPROC_RECVSPACE);
        if(nullptr == m_pRecvData)
        {
            qDebug() << "[nodeClient::initAndConnect] m_pRecvData malloc faild";
            break;
        }

		// 创建登录套接字
        if(false == connect2Server(CONNECT_TYPE_LOGIN, m_nProc))
        {
            qDebug() << "[nodeClient::initAndConnect] create CONNECT_TYPE_LOGIN faild";
            break;
        }

        qDebug() << "[nodeClient::initAndConnect] create CONNECT_TYPE_LOGIN done!";
        //std::this_thread::sleep_for(std::chrono::seconds(1));

        // 创建心跳套接字
        if(false == connect2Server(CONNECT_TYPE_HEART, m_nHeart))
        {
            qDebug() << "[nodeClient::initAndConnect] create CONNECT_TYPE_HEART faild";
            break;
        }

        qDebug() << "[nodeClient::initAndConnect] create CONNECT_TYPE_HEART done!";

		// 创建处理线程
		try
		{
            m_bExit = false;
			m_threadHeartBeat = std::thread(&nodeClient::threadHeartBeatAndRecv, this, m_pRecvData);
            m_threadSendMsg = std::thread(&nodeClient::threadSendMsg, this);
		}
		catch(...)
		{
			qDebug() << "[nodeClient::initAndConnect] create threads faild";
            break;
		}
		
		qDebug() << "[nodeClient::initAndConnect] create threads done!";

		m_bInitStatus = true;
	} while (0);
	
	return m_bInitStatus;
}

// 断开客户端已有连接
bool nodeClient::disconnect()
{
    if(false == m_bInitStatus)
    {
        return true;
    }

    // 发送退出信号给服务端
    ready2close();

    // 释放资源
    release();
    // 关闭套接字
    closeSocket();

    // 更新初始化状态
    m_bInitStatus = false;

    return (!m_bInitStatus);
}

// 获取服务端状态
bool nodeClient::getStatus()
{
    return m_bInitStatus;
}

// 插入待发送数据，通知线程异步发送数据
void nodeClient::insertMsg(MSG_INFO &stMsg)
{
    std::unique_lock<std::mutex> lck (m_mtxSendMsg);
    stMsg.strID.assign(m_strNodeID);    // 加上本节点ID
    m_listSendMsg.push_back(stMsg);
    m_condSendMsg.notify_all();
}

// 释放资源
void nodeClient::release()
{
    m_bExit = true;

    {
        std::unique_lock<std::mutex> lck (m_mtxSendMsg);
        m_condSendMsg.notify_all();
    }

    if(m_threadHeartBeat.joinable())
    {
        m_threadHeartBeat.join();
    }

    if(m_threadSendMsg.joinable())
    {
        m_threadSendMsg.join();
    }

    if(nullptr != m_pRecvData)
    {
        free(m_pRecvData);
        m_pRecvData = nullptr;
    }
}

// 发送断开信号
void nodeClient::ready2close()
{
    int iSendLen = gstrNodeOffline.length();
    int iRetLen = MyDataSend(m_nProc, gstrNodeOffline.c_str(), iSendLen, MAX_DATA_SEND_RECV_TIMEOUT_MS);
    if(iRetLen != iSendLen)
    {
        qDebug() << "[nodeClient::ready2close] send msg faild, sendLen: " << iRetLen;
        return;
    }
}

// 关闭套接字
void nodeClient::closeSocket()
{
    // 释放套接字
    if( INVALID_SOCKET != m_nProc )
    {
        closesocket(m_nProc);
        m_nProc = INVALID_SOCKET;
    }

    if( INVALID_SOCKET != m_nHeart )
    {
        closesocket(m_nHeart);
        m_nHeart = INVALID_SOCKET;
    }
}

// 连接到客户端
bool nodeClient::connect2Server(CONNECT_TYPE nType, SOCKET &nSock)
{
	bool bRet = false;
	nSock = INVALID_SOCKET;

	do
	{
        if(CONNECT_TYPE_UNKNOW == nType || m_strServerIP.empty())
        {
            qDebug() <<"[nodeClient::connect2Server] params check: " << nType << m_strServerIP.length();
            break;
        }

        // 创建套接字
        nSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(INVALID_SOCKET == nSock)
        {
            qDebug() <<"[nodeClient::connect2Server] socket create faild";
            break;
        }

        struct sockaddr_in stAddr;
        memset(&stAddr, 0x0, sizeof(struct sockaddr_in));
        // 端口复用
        if(SOCKET_ERROR == setsockopt(nSock, SOL_SOCKET, SO_REUSEADDR, (const char*)&stAddr, sizeof(int)))
        {
            qDebug() <<"[nodeClient::connect2Server] setsockopt faild";
            break;
        }

        stAddr.sin_family = AF_INET;
        stAddr.sin_addr.S_un.S_addr = inet_addr(m_strServerIP.c_str());
        stAddr.sin_port = htons(m_nServerPort);
        if(SOCKET_ERROR == ::connect(nSock, (struct sockaddr *)&stAddr, sizeof(stAddr)) &&
           WSAEWOULDBLOCK != WSAGetLastError() && WSAEINPROGRESS != WSAGetLastError())
        {
            qDebug() <<"[nodeClient::connect2Server] connect to server faild";
            break;
        }

        // 创建类型报文
        std::string strMsg;
        if(false == getConnectMsg(nType, strMsg))
        {
            qDebug() <<"[nodeClient::connect2Server] getConnectMsg faild";
            break;
        }

		// 发送套接字类型确认报文
        int nMsgLen = strMsg.length();
        int nSize = MyDataSend(nSock, strMsg.c_str(), nMsgLen, MAX_DATA_SEND_RECV_TIMEOUT_MS);
        if(nSize != nMsgLen)
        {
            qDebug() <<"[nodeClient::connect2Server] MyDataSend strMsg faild";
            break;
        }

		// 接收反馈结果
        memset(m_pRecvData, 0x0, sizeof(char) * NODEPROC_RECVSPACE);
        int nRet = MyDataRecv(nSock, m_pRecvData, NODEPROC_RECVSPACE, MAX_DATA_SEND_RECV_TIMEOUT_MS);
        if(0 == nRet)
        {
            qDebug() <<"[nodeClient::connect2Server] MyDataRecv strMsg faild" << nRet;
            break;
        }

		// 确认是否为应答报文
        if(false == parseConnectMsg(m_pRecvData))
        {
            std::string strAck(m_pRecvData);
            qDebug() <<"[nodeClient::connect2Server] MyDataRecv strMsg check faild, msg: " << QString::fromStdString(strAck);
            break;
        }

        //qDebug() <<"[nodeClient::connect2Server] create socket done! type: " << nType;

		bRet = true;
	} while (0);

    // 创建失败，销毁已创建的套接字
    if(false == bRet && INVALID_SOCKET != nSock)
    {
        closesocket(nSock);
        nSock = INVALID_SOCKET;
    }

	return bRet;
}

// 生成连接报文
bool nodeClient::getConnectMsg(CONNECT_TYPE nType, std::string &strMsg)
{
	bool bRet = false;
    cJSON *pRoot = nullptr;
    char* pRet = nullptr;

	do
	{
        strMsg.clear();
        if(CONNECT_TYPE_UNKNOW == nType || m_strServerIP.empty())
        {
            break;
        }

        pRoot = cJSON_CreateObject();
        if(nullptr == pRoot)
        {
            break;
        }

        cJSON_AddNumberToObject(pRoot, NET_DES_NETTYPE, nType);
        cJSON_AddStringToObject(pRoot, NET_DES_NODEID, m_strNodeID.c_str());
        cJSON_AddStringToObject(pRoot, NET_DES_CHECKKEY, m_strCheckKey.c_str());

        pRet = cJSON_PrintUnformatted(pRoot);
        if(nullptr != pRet)
        {
            strMsg.assign(pRet);
        }
        else
        {
            break;
        }

		bRet = true;
	} while (0);
	
	if(nullptr != pRoot)
    {
        cJSON_Delete(pRoot);
        pRoot = nullptr;
    }

    if(nullptr != pRet)
    {
        free(pRet);
        pRet = nullptr;
    }

	return bRet;
}

bool nodeClient::parseConnectMsg(const std::string &strMsg)
{
    cJSON *pRoot = nullptr;
    bool bStatus = false;
    do
    {
        if(strMsg.empty())
        {
            break;
        }

        pRoot = cJSON_Parse(strMsg.c_str());
        if(nullptr == pRoot)
        {
            break;
        }

        cJSON *pnetLinkStatus = cJSON_GetObjectItem(pRoot, NET_DES_LINK);
        if( nullptr == pnetLinkStatus)
        {
            break;
        }

        bStatus = pnetLinkStatus->valueint;

    }while(0);

    if(nullptr != pRoot)
    {
        cJSON_Delete(pRoot);
        pRoot = nullptr;
    }

    return bStatus;
}

// 心跳线程
void nodeClient::threadHeartBeatAndRecv(char *pRecvData)
{
	if(nullptr == pRecvData)
	{
		qDebug() << "[nodeClient::threadHeartBeatAndRecv] pRecvData is null: " << pRecvData;
		return;
	}

    if( INVALID_SOCKET == m_nHeart )
    {
        return;
    }

    int iCnt = 0;
    while (!m_bExit)
    {
        memset(pRecvData, 0x0, sizeof(char) * NODEPROC_RECVSPACE);
        int nRet = MyDataRecv(m_nHeart, pRecvData, NODEPROC_RECVSPACE, MAX_DATA_SEND_RECV_TIMEOUT_MS);
        if (0 == nRet)
        {
            // 未接收到数据
            iCnt++;
            if(iCnt >= NODEPROC_TRTTIMES)
            {
			   	qDebug() << "[nodeClient::threadHeartBeatAndRecv] recv data faild curCnt " << iCnt << ", more than " << NODEPROC_TRTTIMES;
            }
        }
        else
        {
            iCnt = 0;
        }

		// 解析数据并上报
        std::list<MSG_INFO> listMsg;
        if(false == parseRecvMsg(pRecvData, listMsg))
        {
            continue;
        }

        {
            std::unique_lock<std::mutex> lck (m_mtxCallBackMsg);
            m_listMsgInfos.insert(m_listMsgInfos.end(), listMsg.begin(), listMsg.end());
            m_conCallBack.notify_all();
        }
    }
}

// 数据发送线程
void nodeClient::threadSendMsg()
{
    if( INVALID_SOCKET == m_nProc )
    {
        return;
    }

    while (!m_bExit)
    {
        std::list<MSG_INFO> listMsg;
        {
            std::unique_lock<std::mutex> lck (m_mtxSendMsg);
            m_condSendMsg.wait_for(lck, std::chrono::seconds(NODEPROC_WAITTIME_S), [&](){return (!m_listSendMsg.empty() || m_bExit);});

            if(!m_listSendMsg.empty())
            {
                listMsg.swap(m_listSendMsg);
            }
        }

        if(true == m_bExit)
        {
            break;
        }

        std::string strSendMsg;
        // 将待发送数据数据生成批量发送报文
        if(!listMsg.empty())
        {
            //qDebug() << "[nodeClient::threadSendMsg] ready to send bufSize: " << listMsg.size();
            if(false == createSendMsg(listMsg, strSendMsg))
            {
                qDebug() << "[nodeClient::threadSendMsg] createSendMsg faild, continue";
                continue;
            }
        }
        else
        {
            strSendMsg = gstrHeartEmptyMsg;  // 无消息，且到了定时时间，则发送空白数据
        }

        // 发送数据
        int iSendLen = strSendMsg.length();
        int iRetLen = MyDataSend(m_nProc, strSendMsg.c_str(), iSendLen, MAX_DATA_SEND_RECV_TIMEOUT_MS);
        if(iRetLen != iSendLen)
        {
            qDebug() << "[nodeClient::threadSendMsg] send msg faild, sendLen: " << iRetLen;
            continue;
        }
    }
}
